package kube

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/clientcmd/api"
	"k8s.io/client-go/tools/remotecommand"
	"os"
	"os/user"
	"strconv"
	"strings"
	"time"
)

type PodDescribeResult struct {
	Name        string
	Namespace   string
	Priority    *int32
	Node        string
	StartTime   time.Time
	Labels      map[string]string
	Annotations map[string]string
	Status      corev1.PodPhase
	IP          string
	Containers  []corev1.Container
	Conditions  map[string]string
	Environment []corev1.EnvVar
}

var kubeConfig *api.Config
var configPath string

// init 初始化
func init() {
	path := os.Getenv("KTL_KUBE_CONFIG")
	if path == "" {
		current, err := user.Current()
		if err != nil {
			fmt.Println("Not found current User,Not load configuration file")
			os.Exit(0)
		}
		path = current.HomeDir + "/.kube/config"
	}
	configPath = path
	file, err := clientcmd.LoadFromFile(path)
	if err != nil {
		_ = fmt.Errorf("failed to read config file: %v", err)
		os.Exit(0)
	}
	kubeConfig = file
}

// KubeConfig 获取当前配置
func KubeConfig() *api.Config {
	return kubeConfig
}

// ConfigPath 路径
func ConfigPath() string {
	return configPath
}

// Namespace 获取当前Namespace
func Namespace() string {
	return KubeConfig().Contexts[KubeConfig().CurrentContext].Namespace
}

// NewKubePath 文件名创建连接
func NewKubePath(filePath string) (*kubernetes.Clientset, error) {
	file, err := clientcmd.LoadFromFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read config file: %v", err)
	}
	kubeConfig = file
	return ConnectByConfig(file)
}

// NewKubeByte 字节流创建连接
func NewKubeByte(configBytes []byte) (*kubernetes.Clientset, error) {
	config, _ := clientcmd.Load(configBytes)
	kubeConfig = config
	return ConnectByConfig(config)
}

// ConnectByConfig 配置创建连接
func ConnectByConfig(config *api.Config) (*kubernetes.Clientset, error) {
	clientConfig := clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{})
	restConfig, _ := clientConfig.ClientConfig()
	client, err := kubernetes.NewForConfig(restConfig)
	return client, err
}

// ListAllPods 列出所有POD
func ListAllPods(clientSet *kubernetes.Clientset, namespace string) ([]corev1.Pod, error) {
	var pods, err = clientSet.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to list pods: %v", err)
	}
	return pods.Items, nil
}

// FilterPods 列出名称包含指定关键字的pod
func FilterPods(clientSet *kubernetes.Clientset, keyword string) ([]corev1.Pod, error) {
	namespace := KubeConfig().Contexts[KubeConfig().CurrentContext].Namespace
	pods, err := ListAllPods(clientSet, namespace)
	if err != nil {
		return nil, err
	}
	var r []corev1.Pod
	for _, i := range pods {
		if strings.Contains(i.Name, keyword) {
			r = append(r, i)
		}
	}
	return r, err
}

// PrintLogPodByFollow 列出并跟随当前POD日志
func PrintLogPodByFollow(clientSet *kubernetes.Clientset, podName string, lines *int64) {
	res := clientSet.CoreV1().Pods(Namespace()).GetLogs(podName, &corev1.PodLogOptions{
		Follow:    true,
		TailLines: lines,
	})
	logs, err := res.Stream(context.Background())
	if err != nil {
		panic(err.Error())
	}
	defer logs.Close()
	_, err = io.Copy(os.Stdout, logs)
	if err != nil {
		panic(err.Error())
	}

}

func DescribePod(clientSet *kubernetes.Clientset, pod corev1.Pod) {
	res, _ := clientSet.CoreV1().Pods(Namespace()).Get(context.TODO(), pod.Name, metav1.GetOptions{})
	conditions := make(map[string]string)
	for _, con := range res.Status.Conditions {
		conditions[string(con.Type)] = string(con.Status)
	}
	env := res.Spec.Containers[0].Env
	result := PodDescribeResult{
		Name:        res.Name,
		Namespace:   res.Namespace,
		Priority:    res.Spec.Priority,
		Node:        res.Spec.NodeName + "/" + res.Status.HostIP,
		StartTime:   res.Status.StartTime.Time,
		Labels:      res.Labels,
		Status:      res.Status.Phase,
		Annotations: res.Annotations,
		IP:          res.Status.PodIP,
		Containers:  res.Spec.Containers,
		Conditions:  conditions,
		Environment: env,
	}
	for i, _ := range result.Containers {
		result.Containers[i].Env = make([]corev1.EnvVar, 0)
	}
	marshal, _ := json.MarshalIndent(result, "", "  ")
	fmt.Println(string(marshal))
}

// ExecCommandPod 执行命令
func ExecCommandPod(clientSet *kubernetes.Clientset, pod corev1.Pod, command string) {
	clientConfig := clientcmd.NewDefaultClientConfig(*KubeConfig(), &clientcmd.ConfigOverrides{})
	restConfig, _ := clientConfig.ClientConfig()
	// 设置要执行的命令
	req := clientSet.CoreV1().RESTClient().Post().Resource("pods").Name(pod.Name).Namespace(Namespace()).SubResource("exec").VersionedParams(&corev1.PodExecOptions{
		Command: []string{command},
		TTY:     true,
		Stdout:  true,
		Stderr:  true,
		Stdin:   true,
	}, scheme.ParameterCodec)

	exec, err := remotecommand.NewSPDYExecutor(restConfig, "POST", req.URL())
	if err != nil {
		panic(err.Error())
	}

	err = exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
		Stderr: os.Stderr,
		Stdout: os.Stdout,
		Stdin:  os.Stdin,
		Tty:    true,
	})
	if err != nil {
		panic(err.Error())
	}
}

// PrintPod 打印POD信息
func PrintPod(clientSet *kubernetes.Clientset, name string) []corev1.Pod {
	var pods []corev1.Pod
	if len(name) == 0 {
		pods, _ = ListAllPods(clientSet, Namespace())
	} else {
		pods, _ = FilterPods(clientSet, name)
	}
	headerFormat := fmt.Sprintf("%%-%ds %%-%ds %%-%ds %%-%ds  %%-%ds %%-%ds  %%-%ds %%-%ds %%-%ds %%-%ds\n", 4, 60, 10, 10, 12, 15, 20, 20, 20, 20)
	fmt.Printf(headerFormat, "No", "Name", "Ready", "Status", "Restarts", "Age", "POD-IP", "HOST-IP", "CreateTime", "Namespace")
	for index, pod := range pods {
		var totalRestarts int32
		if pod.Status.ContainerStatuses != nil {
			for _, containerStatus := range pod.Status.ContainerStatuses {
				totalRestarts += containerStatus.RestartCount
			}
		} else {
			totalRestarts = 0
		}
		age := time.Now().Sub(pod.CreationTimestamp.Time).Truncate(time.Second)
		no := index + 1
		var readyCount = 0
		for _, condition := range pod.Status.Conditions {
			if condition.Type == corev1.PodReady && condition.Status == "True" {
				readyCount++
			}
		}
		var ready = strconv.Itoa(readyCount) + "/" + strconv.Itoa(len(pod.Spec.Containers))
		fmt.Printf(headerFormat, strconv.FormatInt(int64(no), 10), pod.Name, ready, string(pod.Status.Phase), strconv.FormatInt(int64(totalRestarts), 10), age.String(), pod.Status.PodIP, pod.Status.HostIP, pod.CreationTimestamp.Format(time.DateTime), pod.Namespace)
	}
	return pods
}

// WriteConfig 更新配置
func WriteConfig(config api.Config, fileName string) {
	clientcmd.WriteToFile(config, fileName)
}
